#pragma once

#include "AxisAlignedBox.h"

#include "ExUtilityFunction.h"

#include "3rd_part\xml\tinyxml.h"

class INode;
class ExMesh
{
public:
	ExMesh(void);
	~ExMesh(void);

	/**
	*	The vertex format captured from 3dsMax.
	*/
	struct BloatVertex
	{
		Point3	pos;
		Point3	normal;
		Point2	uv;
		Point3	tangent;
		Point3	binormal;

		int		vertexIndex;
		int		smoothingGroup;
		int		meshIndex;			// for creating MeshParticles
		bool operator == ( const BloatVertex& bv ) const 
		{ 
			return  this->pos == bv.pos &&
				this->normal == bv.normal &&
				this->uv == bv.uv &&
				this->vertexIndex == bv.vertexIndex &&
				(this->smoothingGroup & bv.smoothingGroup) &&
				this->binormal == bv.binormal &&
				this->tangent == bv.tangent &&
				this->meshIndex == bv.meshIndex;
		};

		bool operator < ( const BloatVertex& bv ) const 
		{
			if( pos < bv.pos )	return true;
			if( pos > bv.pos )	return false;

			if( normal < bv.normal )	return true;
			if( normal > bv.normal )	return false;

			if( uv < bv.uv )	return true;
			if( uv > bv.uv )	return false;

			if( vertexIndex < bv.vertexIndex )	return true;
			if( vertexIndex > bv.vertexIndex )	return false;

			if( binormal < bv.binormal )	return true;
			if( binormal > bv.binormal )	return false;

			if( tangent < bv.tangent )	return true;
			if( tangent > bv.tangent )	return false;

			if( meshIndex < bv.meshIndex )	return true;
			if( meshIndex > bv.meshIndex )	return false;

			return false; // enough
		};

		#define BV_FVF D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX2|D3DFVF_TEXCOORDSIZE2(0)|D3DFVF_TEXCOORDSIZE3(1)
	};

	/**
	*	The triangle format captured from 3dsMax.
	*/
	struct Triangle
	{
		int		index[3];
		int		realIndex[3];
		int		smoothingGroup;
		int		materialIndex;
	};

	/**
	*	The material format captured from 3dsMax.
	*/
	struct Material
	{
		Material()
			: identifier( "empty" ),
			mapIdMeaning( 0 ),
			selfIllum( 0 ),
			inUse( false )
		{
		}

		//return all resources used
		void resources( std::vector<std::string>& retResources );

		std::string identifier;
		std::string mapIdentifier;
		int mapIdMeaning;	// 0 = none, 1 = bitmap, 2 = mfm
		float selfIllum;
		Color ambient;
		Color diffuse;
		Color specular;
		bool inUse;

		typedef std::pair< std::string, std::string > TextureOverride;
		typedef std::pair< std::string, Point4 > VectorOverride;
		typedef std::pair< std::string, BOOL > BoolOverride;
		typedef std::pair< std::string, float > FloatOverride;
		typedef std::pair< std::string, int > IntOverride;

		typedef std::vector< TextureOverride > TextureOverrides;
		typedef std::vector< VectorOverride > VectorOverrides;
		typedef std::vector< BoolOverride > BoolOverrides;
		typedef std::vector< FloatOverride > FloatOverrides;
		typedef std::vector< IntOverride > IntOverrides;

		TextureOverrides textureOverrides_;
		VectorOverrides vectorOverrides_;
		BoolOverrides boolOverrides_;
		FloatOverrides floatOverrides_;
		IntOverrides intOverrides_;

		std::string fxFile_;

		bool operator==( const Material& m ) const
		{
			return identifier == m.identifier &&
				mapIdMeaning == m.mapIdMeaning &&
				mapIdentifier == m.mapIdentifier &&
				selfIllum == m.selfIllum &&
				fxFile_ == m.fxFile_ &&
				textureOverrides_ == m.textureOverrides_ &&
				vectorOverrides_ == m.vectorOverrides_ &&
				boolOverrides_ == m.boolOverrides_ &&
				floatOverrides_ == m.floatOverrides_ &&
				intOverrides_ == m.intOverrides_;

		}

		enum SkinType
		{
			NO_SKIN,
			SOFT_SKIN
		};

		/*
		void save( DataSectionPtr pGeometry,
			DataSectionPtr spExistingVisual,
			uint32 pgid,
			SkinType skin = NO_SKIN,
			bool skinnedShader = false );

		bool findAndCopyExistingMaterial( const std::string& identifier,
			DataSectionPtr pPrimGroup,
			DataSectionPtr spExistingVisual ) const;

		DataSectionPtr findExistingMaterial( const std::string& identifier,
			DataSectionPtr spExistingVisual ) const;
			*/

		bool checkFilename( std::string& fxName, const std::string& fxPostfix );
	};

	// Type definitions
	typedef std::vector< BloatVertex >	BVVector;
	typedef std::vector< Triangle >		TriangleVector;
	typedef std::vector< Material >		MaterialVector;

	typedef std::map< BloatVertex, std::set< BVVector::size_type > >		NoneDuplicateVerticesMap;
	typedef std::map< BVVector::size_type, std::vector< BVVector::size_type > >		NormalInfluencedVerticesIndexMap;

	bool Init( INode* pMaxNode, Mesh* pMaxMesh, TimeValue timeNow, bool bSnapVertices );

	virtual bool Save( const std::string& rModelFilePath, TiXmlNode* pContentSection );

	BVVector GetVertices( void ) const { return m_vVertices; }
	Matrix3 GetWorldTM( void ) const { return m_matWorld; }

	const Common::AxisAlignedBox& GetBoundingBox( void ) const;

	void Merge( ExMesh* pDestMesh );

private:

	void _FlipTriangleWindingOrder();
	int  _AddVertex( const BloatVertex& bv );
	void _CreateNormals();
	void _AddNormal( const Point3& normal, int realIndex, int smoothingGroup, int index );

protected:

	//mesh name
	std::string m_strMeshId;
	//filter out the duplicate vertices
	NoneDuplicateVerticesMap m_mapUniqueVertices;
	//used to build correct normal by influenced vertex
	NormalInfluencedVerticesIndexMap m_mapNormalInfluencedVerticesIndex;
	//exported vertices
	BVVector m_vVertices;

	TriangleVector m_vTriangles;
	LPD3DXMESH	m_pD3DXMESH;

	Matrix3 m_matWorld;
	Common::AxisAlignedBox m_aabb;
};

